# -*- coding:utf-8 -*-
from typing import TypeVar, Generic
from pydantic import BaseModel
from fastapi import APIRouter, Depends, Request, Security, File, UploadFile, Form

from utils.response import ResponsePage, ResponseModel, Page
from utils.dependence import get_page_filter, PageFilter
from utils.file import save_file, calculate_md5, merge_file, calculate_object_md5, remove_file

from core.exceptions import ServiceException

from services.user import check_login_user
from services.attachment import AttachmentService
from schemas.attachment import Attachment, AttachmentCreate, AttachmentUpdate, AttachmentSegment


router = APIRouter(prefix="/attachment")

@router.get("", response_model=ResponsePage[Attachment], summary="附件列表查询", dependencies=[Security(check_login_user, scopes=["admin:attachment:access"])])
async def pages(page_no: int=1, page_size: int=10, page_filter = Depends(get_page_filter), service: AttachmentService = Depends(AttachmentService)):
    page = await service.pages(page_no, page_size, page_filter)
    return ResponsePage.successed(data=page)

@router.get("/{id}", response_model=ResponseModel[Attachment], summary="附件信息查询", dependencies=[Security(check_login_user, scopes=["admin:attachment:access"])])
async def attachment_by_id(id: int, service: AttachmentService = Depends(AttachmentService)):
    data_db = await service.get_by_id(id)
    return ResponseModel.successed(data=data_db)

@router.post("", response_model=ResponseModel[Attachment], summary="附件信息新增", dependencies=[Security(check_login_user, scopes=["admin:attachment:create"])]
    )
async def do_create_attachment(request: Request, data: AttachmentCreate, service: AttachmentService = Depends(AttachmentService)):
    data_db = await service.create(request, data)
    return ResponseModel.successed(message="附件信息新增成功", data=Attachment.from_orm(data_db))

@router.post("/upload/{id}", response_model=ResponseModel[Attachment], summary="附件上传", dependencies=[Security(check_login_user, scopes=["admin:attachment:create"])]
    )
async def do_upload_attachment(request: Request, id: int, file: UploadFile = File(), service: AttachmentService = Depends(AttachmentService)):
    check_data = await service.get_by_id(id)
    if check_data is None:
        raise ServiceException(code=404, message="Attachment not found")
    if check_data.hash is None or check_data.hash.upper() != calculate_md5(file).upper():
        raise ServiceException(code=422, message="File hash not match")
    if check_data.status != 2:
        # raise ServiceException(code=409, message="Attachment already uploaded")
        url = f"{check_data.category}/{check_data.hash}.{check_data.name.rsplit('.', 1)[-1]}"
        url = await save_file(file, f"data/{url}", file.content_type)
        check_data = await service.update(request, id, Attachment.from_orm({'status': 2, 'size': file.size, 'content_type': file.content_type, 'url': url}))
        
    return ResponseModel.successed(data=Attachment.from_orm(check_data), message="Upload success")

@router.delete("/{id}", response_model=ResponseModel[Attachment], summary="附件信息删除", dependencies=[Security(check_login_user, scopes=["admin:attachment:delete"])])
async def do_delete_attachment(request: Request, id: int, service: AttachmentService = Depends(AttachmentService)):
    data_db = await service.delete(request, id)
    return ResponseModel.successed(message="附件信息删除成功", data=Attachment.from_orm(data_db))


@router.put("/{id}", response_model=ResponseModel[Attachment], summary="附件信息更新", dependencies=[Security(check_login_user, scopes=["admin:attachment:update"])])
async def do_update_attachment(request: Request, id: int, data: AttachmentUpdate, service: AttachmentService = Depends(AttachmentService)):
    data_db = await service.update(request, id, data)
    return ResponseModel.successed(message="附件信息更新成功", data=Attachment.from_orm(data_db))


@router.post("/create/segments/{id}", response_model=ResponseModel[list[AttachmentSegment]], summary="附件分片创建"
    # 
    )
async def do_create_attachment_segments(request: Request, id: int, segments: list[AttachmentSegment], service: AttachmentService = Depends(AttachmentService)):
    check_data = await service.get_by_id(id)
    if check_data is None:
        raise ServiceException(code=404, message="Attachment not found")
    
    # redis记录当前批次的分片信息
    segements_dict = {item.hash: item.status for item in segments}
    for item in segments:
        key = f'segments:{check_data.hash}:{item.hash}'
        segment_info = await request.app.state.redis.get(key)
        if segment_info is None:
            await request.app.state.redis.set(key, segements_dict[item.hash])
        else:
            segements_dict[item.hash] = segment_info
    
    for item in segments:
        item.status = int(segements_dict[item.hash])
    
    return ResponseModel.successed(data=segments, message="segements create success")

@router.post("/upload/segments/{id}", response_model=ResponseModel[Attachment], summary="附件分片上传", dependencies=[Security(check_login_user, scopes=["admin:attachment:upload"])])
async def do_upload_attachment_segments(request: Request, id: int, file: UploadFile = File(), index: str = Form(), hash: str = Form(), service: AttachmentService = Depends(AttachmentService)):
    check_data = await service.get_by_id(id)
    if check_data is None:
        raise ServiceException(code=404, message="Attachment not found")
    url = f"{check_data.category}/{check_data.hash}/{index}"
    url = await save_file(file, f"data/{url}", file.content_type)
    # 设置该分片已上传
    await request.app.state.redis.set(f'segments:{check_data.hash}:{hash}', 1)

    return ResponseModel.successed(data=Attachment.from_orm(check_data), message="Upload success")

@router.post("/merge/segments/{id}", response_model=ResponseModel[Attachment], summary="附件分片上传合并", dependencies=[Security(check_login_user, scopes=["admin:attachment:merge"])])
async def do_merge_attachment_segments(request: Request, id: int, service: AttachmentService = Depends(AttachmentService)):
    check_data = await service.get_by_id(id)
    if check_data is None:
        raise ServiceException(code=404, message="Attachment not found")
    if check_data.hash is None:
        raise ServiceException(code=422, message="File hash not match")
    if check_data.status != 2: # 2: 分片上传完成
        filename = f"data/{check_data.category}/{check_data.hash}.{check_data.name.rsplit('.', 1)[-1]}"
        path = f"data/{check_data.category}/{check_data.hash}/"
        # 合并文件
        url = await merge_file(filename, path, check_data.content_type) 
        if url is None:
            raise ServiceException(code=500, message="Merge file failed")
        # 判断文件hash是否一致
        hash = await calculate_object_md5(filename)
        if hash is None or check_data.hash.upper() != hash.upper():
            raise ServiceException(code=422, message="File hash not match")
        
        # redis删除分片信息
        # 使用 SCAN 命令扫描匹配的子键
        cursor = 0
        while True:
            cursor, keys = await request.app.state.redis.scan(cursor=cursor, match=f'segments:{check_data.hash}:*', count=100)
            if keys:
                # 删除找到的所有子键
                await request.app.state.redis.delete(*keys)
                print(f"Deleted keys: {keys}")
            
            # 如果游标为0，表示扫描完成
            if cursor == 0:
                break
        check_data = await service.update(request, id, Attachment.from_orm({'status': 2, 'url': url}))
        # minio删除文件
        remove_file_result = await remove_file(path)

    return ResponseModel.successed(data=Attachment.from_orm(check_data), message="Upload success")
